use async_trait::async_trait;

use crate::{regex::regex2, AnyRes as _, AnyResult, Error};
use std::{
  borrow::Cow,
  fs,
  io::{self, Write as _},
  path::{Path, PathBuf},
};
/// 同步树形目录，返回完整路径的字符串数组
pub fn tree_from(src: impl AsRef<str>, is_next: bool) -> crate::AnyResult<Vec<PathBuf>> {
  let path = PathBuf::from(src.as_ref());

  // 处理文件情况
  if path.is_file() {
    return Ok(vec![path]);
  }

  // 获取目标目录和匹配模式
  let (target_dir, pattern) = if path.is_dir() {
    (path, None)
  } else if let Some(parent) = path.parent() {
    if !parent.is_dir() {
      return Err(format!("{} 不是一个有效的路径", parent.display()).into());
    }
    (parent.to_path_buf(), path.file_name().map(|f| f.to_string_lossy().to_string()))
  } else {
    return Err(format!("{} 不是一个有效的路径", src.as_ref()).into());
  };

  // 读取目录内容
  let files = if is_next {
    tree_folder(&target_dir)?
  } else {
    let mut list = Vec::new();
    for entry in std::fs::read_dir(&target_dir)? {
      list.push(entry?.path());
    }
    list
  };

  // 根据pattern过滤结果
  Ok(match pattern {
    Some(pat) => files.into_iter().filter(|v| regex2(&v.display().to_string(), &pat).0).collect(),
    None => files,
  })
}

/// 树形目录，返回完整路径的字符串数组
pub fn tree_folder<P>(dir_path: P) -> AnyResult<Vec<PathBuf>>
where
  P: AsRef<Path>,
{
  let path = dir_path.as_ref();
  if !path.exists() {
    return Err(format!("Path does not exist: {}", path.display()).into());
  }
  let mut result = Vec::new();
  if path.is_dir() {
    result.extend(fs::read_dir(path)?.filter_map(|entry| entry.ok()).flat_map(|entry| {
      let path = entry.path();
      if path.is_dir() {
        tree_folder(path).unwrap_or_default()
      } else {
        vec![path]
      }
    }));
  } else {
    result.push(path.to_path_buf());
  }

  Ok(result)
}

/// 解析字符串中的,
pub fn tree_from_str(src: impl AsRef<str>) -> Vec<String> {
  src.as_ref().split(',').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect()
}
/// 正则匹配文件夹中的文件
pub fn regex_read_dir(src: impl AsRef<Path>, pat: &str) -> crate::Result<Vec<String>> {
  Ok(
    fs::read_dir(src.as_ref())
      .any()?
      .filter_map(Result::ok)
      .filter_map(|entry| {
        let path = entry.path();
        path
          .is_file()
          .then(|| {
            let path_str = path.to_string_lossy().to_string();
            let (is_success, res) = regex2(&path_str, pat);
            if is_success {
              res.map(|v| v.to_string())
            } else {
              Some(path_str)
            }
          })
          .flatten()
      })
      .collect(),
  )
}
/// 重命名
pub fn rename_file<P, P2>(src: P, dst: P2) -> AnyResult<()>
where
  P: AsRef<Path>,
  P2: AsRef<Path>,
{
  let src = src.as_ref();
  let dst = dst.as_ref();
  if src.exists() && src.is_file() {
    if dst.exists() && !dst.is_file() {
      Err(format!("目标已存在，并非文件格式 {}", dst.display()).into())
    } else {
      fs::rename(src, dst)?;
      if dst.exists() && dst.is_file() {
        Ok(())
      } else {
        Err(format!("源{} 目标移动失败 {}", src.display(), dst.display()).into())
      }
    }
  } else {
    Err(format!("原始缓存文件不存在 {}", src.display()).into())
  }
}
/// 自动化转换路径
pub fn convert_path(path_str: &str) -> String {
  if cfg!(target_os = "windows") {
    path_str.replace('/', "\\")
  } else {
    String::from(path_str)
  }
}

/// 复制目录
pub fn auto_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> AnyResult<()> {
  let from_path = from.as_ref();
  let to_path = to.as_ref();

  if from_path.is_file() {
    // 如果是文件，直接复制
    fs::copy(from_path, to_path)?;
  } else if from_path.is_dir() {
    // to_path.auto_create_dir()?;
    // 遍历目录中的所有条目
    for entry in fs::read_dir(from_path)? {
      let entry = entry?;
      let from_entry_path = entry.path();
      let to_entry_path = to_path.join(from_entry_path.file_name().ok_or(Error::Str("无法解析文件名".into()))?);
      // 递归复制每个条目
      auto_copy(from_entry_path, to_entry_path)?;
    }
  } else {
    // 如果既不是文件也不是目录，返回错误
    return Err("Path is neither a file nor a directory".into());
  }
  Ok(())
}

/// 写入
fn write<'a>(path: &PathBuf, bytes: Cow<'a, [u8]>, is_sync: bool, is_append: bool) -> AnyResult<()> {
  if !is_append && path.exists() {
    // path.auto_remove_file()?;
  }
  let mut f = fs::OpenOptions::new().read(true).write(true).create(true).append(is_append).open(path)?;
  f.write_all(&bytes)?;
  if is_sync {
    f.sync_data()?;
  }
  Ok(())
}
/// 写入GBK格式
#[cfg(feature = "encode")]
pub fn write_gbk<'a>(path: &PathBuf, content: &'a str, is_sync: bool, is_append: bool) -> AnyResult<()> {
  let (bytes, _encode, had_errors) = encoding_rs::GBK.encode(content);
  if had_errors {
    return Err("写入GBK失败".into());
  } else {
    write(path, bytes, is_sync, is_append)
  }
}
/// 写入UTF-8格式
#[cfg(feature = "encode")]
pub fn write_utf8<'a>(path: &PathBuf, content: &'a str, is_sync: bool, is_append: bool) -> AnyResult<()> {
  let (bytes, _encode, had_errors) = encoding_rs::UTF_8.encode(content);
  if had_errors {
    return Err("写入UTF-8失败".into());
  } else {
    write(path, bytes, is_sync, is_append)
  }
}
/// 异步写入
#[cfg(feature = "tokio")]
pub mod a_sync {
  use crate::{regex::regex2, AnyRes as _, AnyResult};
  use std::{
    borrow::Cow,
    path::{Path, PathBuf},
  };
  use tokio::{fs, io::AsyncWriteExt as _};

  /// 异步树形目录，返回完整路径的字符串数组
  pub async fn a_tree_from(src: impl AsRef<str>, is_next: bool) -> crate::AnyResult<Vec<PathBuf>> {
    let path = PathBuf::from(src.as_ref());
    // 处理文件情况
    if path.is_file() {
      return Ok(vec![path]);
    }
    // 获取目标目录和匹配模式
    let (target_dir, pattern) = if path.is_dir() {
      (path, None)
    } else if let Some(parent) = path.parent() {
      if !parent.is_dir() {
        return Err(format!("{} 不是一个有效的路径", parent.display()).into());
      }
      (parent.to_path_buf(), path.file_name().map(|f| f.to_string_lossy().to_string()))
    } else {
      return Err(format!("{} 不是一个有效的路径", src.as_ref()).into());
    };

    // 读取目录内容
    let files = if is_next {
      a_tree_folder(&target_dir).await?
    } else {
      let mut list = Vec::new();
      let mut dir = tokio::fs::read_dir(&target_dir).await?;
      while let Some(entry) = dir.next_entry().await? {
        list.push(entry.path());
      }
      list
    };

    // 根据pattern过滤结果
    Ok(match pattern {
      Some(pat) => files.into_iter().filter(|v| regex2(&v.display().to_string(), &pat).0).collect(),
      None => files,
    })
  }

  /// 异步树形目录，返回完整路径的字符串数组
  pub async fn a_tree_folder<P>(dir_path: P) -> AnyResult<Vec<PathBuf>>
  where
    P: AsRef<Path>,
  {
    Box::pin(async move {
      let path = dir_path.as_ref();
      if !path.exists() {
        return Err(format!("Path does not exist: {}", path.display()).into());
      }
      let mut result = Vec::new();
      if path.is_dir() {
        let mut read_dir = tokio::fs::read_dir(path).await?;
        while let Some(entry) = read_dir.next_entry().await? {
          let path = entry.path();
          if path.is_dir() {
            let sub_files = a_tree_folder(&path).await?;
            result.extend(sub_files);
          } else {
            result.push(path);
          }
        }
      } else {
        result.push(path.to_path_buf());
      }
      Ok(result)
    })
    .await
  }

  /// 异步正则匹配文件夹中的文件
  pub async fn a_regex_read_dir(src: impl AsRef<Path>, pat: &str) -> crate::Result<Vec<String>> {
    let mut entries = tokio::fs::read_dir(src.as_ref()).await.any()?;
    let mut results = Vec::new();
    while let Some(entry) = entries.next_entry().await.any()? {
      let path = entry.path();
      if path.is_file() {
        let path_str = path.to_string_lossy().to_string();
        let (is_success, res) = regex2(&path_str, pat);
        if is_success {
          if let Some(v) = res {
            results.push(v.to_string());
          }
        } else {
          results.push(path_str);
        }
      }
    }

    Ok(results)
  }

  /// 异步写入
  async fn write<'a>(path: &PathBuf, bytes: Cow<'a, [u8]>, is_sync: bool, is_append: bool) -> AnyResult<()> {
    if !is_append && path.exists() {
      fs::remove_file(path).await?
    }
    let mut f = fs::OpenOptions::new().read(true).write(true).create(true).append(is_append).open(path).await?;
    f.write_all(&bytes).await?;
    if is_sync {
      f.sync_data().await?;
    }
    Ok(())
  }
  /// 异步写入GBK格式
  #[cfg(feature = "encode")]
  pub async fn write_gbk<'a>(path: &PathBuf, content: &'a str, is_sync: bool, is_append: bool) -> AnyResult<()> {
    let (bytes, _encode, had_errors) = encoding_rs::GBK.encode(content);
    if had_errors {
      return Err("异步写入GBK失败".into());
    } else {
      write(path, bytes, is_sync, is_append).await
    }
  }
  /// 异步写入UTF-8格式
  #[cfg(feature = "encode")]
  pub async fn write_utf8<'a>(path: &PathBuf, content: &'a str, is_sync: bool, is_append: bool) -> AnyResult<()> {
    let (bytes, _encode, had_errors) = encoding_rs::UTF_8.encode(content);
    if had_errors {
      return Err("异步写入UTF-8失败".into());
    } else {
      write(path, bytes, is_sync, is_append).await
    }
  }
}

/// 自动检查和操作文件系统路径
#[async_trait]
pub trait AutoPath {
  /// 自动创建目录
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// let path = Path::new("test_dir");
  /// path.auto_create_dir().unwrap();
  /// assert!(path.exists() && path.is_dir());
  ///
  /// // 清理
  /// std::fs::remove_dir(path).unwrap();
  /// ```
  fn auto_create_dir(&self) -> AnyResult<()>;

  /// 自动移除目录
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// let path = Path::new("test_remove_dir");
  /// std::fs::create_dir(path).unwrap();
  /// assert!(path.exists());
  ///
  /// path.auto_remove_dir().unwrap();
  /// assert!(!path.exists());
  /// ```
  fn auto_remove_dir(&self) -> AnyResult<()>;

  /// 自动创建文件
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// let path = Path::new("test_file.txt");
  /// path.auto_create_file("Hello, World!").unwrap();
  /// assert!(path.exists() && path.is_file());
  ///
  /// let content = std::fs::read_to_string(path).unwrap();
  /// assert_eq!(content, "Hello, World!");
  ///
  /// // 清理
  /// std::fs::remove_file(path).unwrap();
  /// ```
  fn auto_create_file<S: AsRef<str>>(&self, content: S) -> AnyResult<()>;

  /// 自动移除文件
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// let path = Path::new("test_remove_file.txt");
  /// std::fs::write(path, "Test content").unwrap();
  /// assert!(path.exists());
  ///
  /// path.auto_remove_file().unwrap();
  /// assert!(!path.exists());
  /// ```
  fn auto_remove_file(&self) -> AnyResult<()>;

  /// Asynchronously creates a directory.
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// #[tokio::main]
  /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
  ///     let path = Path::new("test_async_dir");
  ///     path.a_auto_create_dir().await?;
  ///     assert!(path.exists() && path.is_dir());
  ///
  ///     // Clean up
  ///     tokio::fs::remove_dir(path).await?;
  ///     Ok(())
  /// }
  /// ```
  #[cfg(feature = "tokio")]
  async fn a_auto_create_dir(&self) -> AnyResult<()>;

  /// Asynchronously removes a directory.
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// #[tokio::main]
  /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
  ///     let path = Path::new("test_async_remove_dir");
  ///     tokio::fs::create_dir(path).await?;
  ///     assert!(path.exists());
  ///
  ///     path.a_auto_remove_dir().await?;
  ///     assert!(!path.exists());
  ///     Ok(())
  /// }
  /// ```
  #[cfg(feature = "tokio")]
  async fn a_auto_remove_dir(&self) -> AnyResult<()>;

  /// Asynchronously creates a file with the given content.
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// #[tokio::main]
  /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
  ///     let path = Path::new("test_async_file.txt");
  ///     path.a_auto_create_file("Hello, World!").await?;
  ///     assert!(path.exists() && path.is_file());
  ///
  ///     let content = tokio::fs::read_to_string(path).await?;
  ///     assert_eq!(content, "Hello, World!");
  ///
  ///     // Clean up
  ///     tokio::fs::remove_file(path).await?;
  ///     Ok(())
  /// }
  /// ```
  #[cfg(feature = "tokio")]
  async fn a_auto_create_file(&self, content: impl AsRef<[u8]> + Send) -> AnyResult<()>;

  /// Asynchronously removes a file.
  ///
  /// # Example
  ///
  /// ```
  /// use std::path::Path;
  /// use e_utils::fs::AutoPath;
  ///
  /// #[tokio::main]
  /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
  ///     let path = Path::new("test_async_remove_file.txt");
  ///     tokio::fs::write(path, "Test content").await?;
  ///     assert!(path.exists());
  ///
  ///     path.a_auto_remove_file().await?;
  ///     assert!(!path.exists());
  ///     Ok(())
  /// }
  /// ```
  #[cfg(feature = "tokio")]
  async fn a_auto_remove_file(&self) -> AnyResult<()>;
}

#[async_trait]
impl<T: AsRef<str> + Send + Sync> AutoPath for T {
  fn auto_create_dir(&self) -> AnyResult<()> {
    Path::new(self.as_ref()).auto_create_dir()
  }

  fn auto_remove_dir(&self) -> AnyResult<()> {
    Path::new(self.as_ref()).auto_remove_dir()
  }

  fn auto_create_file<S>(&self, content: S) -> AnyResult<()>
  where
    S: AsRef<str>,
  {
    Path::new(self.as_ref()).auto_create_file(content)
  }

  fn auto_remove_file(&self) -> AnyResult<()> {
    Path::new(self.as_ref()).auto_remove_file()
  }
  #[cfg(feature = "tokio")]
  async fn a_auto_create_dir(&self) -> AnyResult<()> {
    Path::new(self.as_ref()).a_auto_create_dir().await
  }
  #[cfg(feature = "tokio")]
  async fn a_auto_remove_dir(&self) -> AnyResult<()> {
    Path::new(self.as_ref()).a_auto_remove_dir().await
  }
  #[cfg(feature = "tokio")]
  async fn a_auto_create_file(&self, content: impl AsRef<[u8]> + Send) -> AnyResult<()> {
    Path::new(self.as_ref()).a_auto_create_file(content).await
  }
  #[cfg(feature = "tokio")]
  async fn a_auto_remove_file(&self) -> AnyResult<()> {
    Path::new(self.as_ref()).a_auto_remove_file().await
  }
}
#[async_trait]
impl AutoPath for Path {
  fn auto_create_dir(&self) -> AnyResult<()> {
    if !self.exists() {
      fs::create_dir_all(self)?;
      if !self.exists() {
        return Err(format!("{} -> {}", self.display(), io::ErrorKind::NotFound).into());
      }
    }
    return Ok(());
  }
  fn auto_remove_dir(&self) -> AnyResult<()> {
    if self.is_dir() {
      fs::remove_dir_all(self)?;
      if self.exists() {
        return Err(format!("{} -> {}", self.display(), io::ErrorKind::AlreadyExists).into());
      }
    } else if self.exists() {
      return Err(format!("{} is not a directory", self.display()).into());
    }
    Ok(())
  }

  fn auto_create_file<S: AsRef<str>>(&self, data: S) -> AnyResult<()> {
    if !self.exists() {
      fs::write(self, data.as_ref())?;
    } else if !self.is_file() {
      return Err(format!("{} -> {}", self.display(), io::ErrorKind::AlreadyExists).into());
    }
    Ok(())
  }

  fn auto_remove_file(&self) -> AnyResult<()> {
    if self.exists() {
      if self.is_file() {
        fs::remove_file(self)?;
      } else {
        return Err(format!("{} -> {}", self.display(), io::ErrorKind::AlreadyExists).into());
      }
    }
    Ok(())
  }
  #[cfg(feature = "tokio")]
  async fn a_auto_create_dir(&self) -> AnyResult<()> {
    if !self.exists() {
      tokio::fs::create_dir_all(self).await?;
      if !self.exists() {
        return Err(format!("{} -> {}", self.display(), io::ErrorKind::NotFound).into());
      }
    }
    return Ok(());
  }

  #[cfg(feature = "tokio")]
  async fn a_auto_remove_dir(&self) -> AnyResult<()> {
    if self.is_dir() {
      tokio::fs::remove_dir_all(self).await?;
      if self.exists() {
        return Err(format!("{} -> {}", self.display(), io::ErrorKind::AlreadyExists).into());
      }
    } else if self.exists() {
      return Err(format!("{} is not a directory", self.display()).into());
    }
    Ok(())
  }

  #[cfg(feature = "tokio")]
  async fn a_auto_create_file(&self, content: impl AsRef<[u8]> + Send) -> AnyResult<()> {
    if !self.exists() {
      tokio::fs::write(self, content.as_ref()).await?;
    } else if !self.is_file() {
      return Err(format!("{} -> {}", self.display(), io::ErrorKind::AlreadyExists).into());
    }
    Ok(())
  }

  #[cfg(feature = "tokio")]
  async fn a_auto_remove_file(&self) -> AnyResult<()> {
    if self.exists() {
      if self.is_file() {
        tokio::fs::remove_file(self).await?;
      } else {
        return Err(format!("{} -> {}", self.display(), io::ErrorKind::AlreadyExists).into());
      }
    }
    Ok(())
  }
}
/// 创建临时文件
pub fn temp_file(folder: impl AsRef<str>, fname: impl AsRef<str>, content: impl AsRef<str>) -> crate::AnyResult<PathBuf> {
  let path = std::env::temp_dir().join(folder.as_ref());
  path.auto_create_dir()?;
  let target = path.join(fname.as_ref());
  target.auto_create_file(content.as_ref())?;
  Ok(target)
}
/// 创建临时文件
#[cfg(feature = "tokio")]
pub async fn a_temp_file(folder: impl AsRef<str>, fname: impl AsRef<str>, content: impl AsRef<str>) -> crate::AnyResult<PathBuf> {
  let path = std::env::temp_dir().join(folder.as_ref());
  path.a_auto_create_dir().await?;
  let target = path.join(fname.as_ref());
  target.a_auto_create_file(content.as_ref()).await?;
  Ok(target)
}

#[cfg(test)]
mod auto_path_tests {
  use super::*;
  use std::fs;

  #[test]
  fn test_auto_create_dir() {
    let path = temp_file("test", "test_file.txt", "Hello, World!").unwrap();
    assert!(path.exists() && path.parent().unwrap().is_dir());
  }

  #[test]
  fn test_auto_remove_dir() {
    let path = temp_file("test", "test_remove_dir", "Hello, World!").unwrap();
    let parent = path.parent().unwrap();
    parent.auto_remove_dir().unwrap();
    assert!(!parent.exists());
  }

  #[test]
  fn test_auto_create_file() {
    let path = temp_file("test", "test_file.txt", "Hello, World!").unwrap();
    assert!(path.exists() && path.is_file());
    assert_eq!(fs::read_to_string(&path).unwrap(), "Hello, World!");
  }

  #[test]
  fn test_auto_remove_file() {
    let path = temp_file("test", "test_remove_file.txt", "Test content").unwrap();
    let parent = path.parent().unwrap();
    parent.auto_remove_dir().unwrap();
    assert!(!parent.exists());
  }

  #[cfg(feature = "tokio")]
  #[tokio::test]
  async fn test_a_auto_create_dir() {
    let path = temp_file("test", "test_async_dir", "Test content").unwrap();
    path.a_auto_create_dir().await.unwrap();
    assert!(path.exists() && path.is_dir());
  }

  #[cfg(feature = "tokio")]
  #[tokio::test]
  async fn test_a_auto_remove_dir() {
    let path = temp_file("test", "test_async_remove_dir", "Test content").unwrap();
    let parent = path.parent().unwrap();
    parent.a_auto_remove_dir().await.unwrap();
    assert!(!parent.exists());
  }

  #[cfg(feature = "tokio")]
  #[tokio::test]
  async fn test_a_auto_create_file() {
    let path = temp_file("test", "test_async_file.txt", "Hello, Async World!").unwrap();
    assert!(path.exists() && path.is_file());
    assert_eq!(tokio::fs::read_to_string(&path).await.unwrap(), "Hello, Async World!");
  }

  #[cfg(feature = "tokio")]
  #[tokio::test]
  async fn test_a_auto_remove_file() {
    let path = temp_file("test", "test_async_remove_file.txt", "Test async content").unwrap();
    let parent = path.parent().unwrap();
    parent.a_auto_remove_dir().await.unwrap();
    assert!(!parent.exists());
  }
}
